/*-------------------------------------------------------------------------
 *
 * nodeBitmapOr.c
 *	  处理 BitmapOr 节点的例程。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/executor/nodeBitmapOr.c
 *
 *-------------------------------------------------------------------------
 */
/* 接口例程
 *		ExecInitBitmapOr	- 初始化 BitmapOr 节点
 *		MultiExecBitmapOr	- 从节点检索结果位图
 *		ExecEndBitmapOr		- 关闭 BitmapOr 节点
 *		ExecReScanBitmapOr	- 重新扫描 BitmapOr 节点
 *
 *	 注意
 *		BitmapOr 节点不使用其左子树和右子树，
 *		而是维护一个子计划列表，
 *		就像 Append 节点一样。然而，逻辑比
 *		Append 简单得多，因为我们不需要处理
 *		前向/后向执行。
 */

#include "postgres.h"

#include "executor/execdebug.h"
#include "executor/nodeBitmapOr.h"
#include "miscadmin.h"


/* ----------------------------------------------------------------
 *		ExecBitmapOr
 *
 *		形式合规的存根
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_ExecBitmapOr(PlanState *fc_pstate)
{
	elog(ERROR, "BitmapOr node does not support ExecProcNode call convention");
	return NULL;
}

/* ----------------------------------------------------------------
 *		ExecInitBitmapOr
 *
 *		开始所有 BitmapOr 节点的子扫描。
 * ----------------------------------------------------------------
 */
BitmapOrState *
ExecInitBitmapOr(BitmapOr *fc_node, EState *fc_estate, int fc_eflags)
{
	BitmapOrState *fc_bitmaporstate = makeNode(BitmapOrState);
	PlanState **fc_bitmapplanstates;
	int			fc_nplans;
	int			fc_i;
	ListCell   *fc_l;
	Plan	   *fc_initNode;

	/* 检查不支持的标志 */
	Assert(!(fc_eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));

	/*
	 * 设置空的子计划状态向量
	 */
	fc_nplans = list_length(fc_node->bitmapplans);

	fc_bitmapplanstates = (PlanState **) palloc0(fc_nplans * sizeof(PlanState *));

	/*
	 * 为我们的 BitmapOr 节点创建新的 BitmapOrState
	 */
	fc_bitmaporstate->ps.plan = (Plan *) fc_node;
	fc_bitmaporstate->ps.state = fc_estate;
	fc_bitmaporstate->ps.ExecProcNode = fc_ExecBitmapOr;
	fc_bitmaporstate->bitmapplans = fc_bitmapplanstates;
	fc_bitmaporstate->nplans = fc_nplans;

	/*
	 * 对每个要执行的计划调用 ExecInitNode，并将结果保存到
	 * 数组 "bitmapplanstates" 中。
	 */
	fc_i = 0;
	foreach(fc_l, fc_node->bitmapplans)
	{
		fc_initNode = (Plan *) lfirst(fc_l);
		fc_bitmapplanstates[fc_i] = ExecInitNode(fc_initNode, fc_estate, fc_eflags);
		fc_i++;
	}

	/*
	 * 杂项初始化
	 *
	 * BitmapOr 计划没有表达式上下文，因为它们从不调用
	 * ExecQual 或 ExecProject。它们也不需要任何元组插槽。
	 */

	return fc_bitmaporstate;
}

/* ----------------------------------------------------------------
 *	   MultiExecBitmapOr
 * ----------------------------------------------------------------
 */
Node * MultiExecBitmapOr(BitmapOrState *fc_node)
{
	PlanState **fc_bitmapplans;
	int			fc_nplans;
	int			fc_i;
	TIDBitmap  *fc_result = NULL;

	/* 必须提供我们自己的仪器支持 */
	if (fc_node->ps.instrument)
		InstrStartNode(fc_node->ps.instrument);

	/*
	 * 从节点获取信息
	 */
	fc_bitmapplans = fc_node->bitmapplans;
	fc_nplans = fc_node->nplans;

	/*
	 * 扫描所有子计划并 OR 其结果位图
	 */
	for (fc_i = 0; fc_i < fc_nplans; fc_i++)
	{
		PlanState  *fc_subnode = fc_bitmapplans[fc_i];
		TIDBitmap  *fc_subresult;

		/*
		 * 我们可以特殊处理 BitmapIndexScan 子节点，以避免为每个子节点
		 * 执行显式的 tbm_union 步骤：只需传递当前结果位图
		 * 并让子节点直接进入。
		 */
		if (IsA(fc_subnode, BitmapIndexScanState))
		{
			if (fc_result == NULL) /* 第一个子计划 */
			{
				/* XXX 我们应该对这个使用小于work_mem吗？ */
				fc_result = tbm_create(work_mem * 1024L,
									((BitmapOr *) fc_node->ps.plan)->isshared ?
									fc_node->ps.state->es_query_dsa : NULL);
			}

			((BitmapIndexScanState *) fc_subnode)->biss_result = fc_result;

			fc_subresult = (TIDBitmap *) MultiExecProcNode(fc_subnode);

			if (fc_subresult != fc_result)
				elog(ERROR, "unrecognized result from subplan");
		}
		else
		{
			/* 标准实现 */
			fc_subresult = (TIDBitmap *) MultiExecProcNode(fc_subnode);

			if (!fc_subresult || !IsA(fc_subresult, TIDBitmap))
				elog(ERROR, "unrecognized result from subplan");

			if (fc_result == NULL)
				fc_result = fc_subresult; /* 第一个子计划 */
			else
			{
				tbm_union(fc_result, fc_subresult);
				tbm_free(fc_subresult);
			}
		}
	}

	/* 我们可以在这里返回一个空结果集？ */
	if (fc_result == NULL)
		elog(ERROR, "BitmapOr doesn't support zero inputs");

	/* 必须提供我们自己的仪器支持 */
	if (fc_node->ps.instrument)
		InstrStopNode(fc_node->ps.instrument, 0 /* XXX */ );

	return (Node *) fc_result;
}

/* ----------------------------------------------------------------
 *		ExecEndBitmapOr
 *
 *		关闭 BitmapOr 节点的子扫描。
 *
 *		不返回任何重要内容。
 * ----------------------------------------------------------------
 */
void ExecEndBitmapOr(BitmapOrState *fc_node)
{
	PlanState **fc_bitmapplans;
	int			fc_nplans;
	int			fc_i;

	/*
	 * 从节点获取信息
	 */
	fc_bitmapplans = fc_node->bitmapplans;
	fc_nplans = fc_node->nplans;

	/*
	 * 关闭每个已初始化的子扫描
	 */
	for (fc_i = 0; fc_i < fc_nplans; fc_i++)
	{
		if (fc_bitmapplans[fc_i])
			ExecEndNode(fc_bitmapplans[fc_i]);
	}
}

void ExecReScanBitmapOr(BitmapOrState *fc_node)
{
	int			fc_i;

	for (fc_i = 0; fc_i < fc_node->nplans; fc_i++)
	{
		PlanState  *fc_subnode = fc_node->bitmapplans[fc_i];

		/*
		 * ExecReScan 不知道我的子计划，所以我必须自己进行
		 * 变化参数的信号传递。
		 */
		if (fc_node->ps.chgParam != NULL)
			UpdateChangedParamSet(fc_subnode, fc_node->ps.chgParam);

		/*
		 * 如果子节点的 chgParam 不是 null，则计划将通过
		 * 第一个 ExecProcNode 重新扫描。
		 */
		if (fc_subnode->chgParam == NULL)
			ExecReScan(fc_subnode);
	}
}
